package reflex.learning02;

import java8.atguigu.lu.l01.Employee;
import org.junit.Test;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.Period;
import java.util.Properties;

/**
 * @program JavaBase
 * @description:
 * @author: zhanglu
 * @create: 2019-06-07 20:11:00
 *
 * 反射机制提供的功能
 * 1、在运行时判断任意一个额对象所属的类
 * 2、在运行时构造任意一个类的对象
 * 3、在运行时判断任意一个类所具有的成员变量和方法
 * 4、在运行时调用任意一个对象的成员变量和方法
 * 5、生成动态代理
 */
public class TestReflection01 {

    /**
     * 小练一把
     * @throws Exception
     */
    @Test
    public void test01() throws Exception {
        Class<Person> clazz = Person.class;

        //创建clazz对应的运行时类Personb的对象
        Person p = clazz.newInstance();
        Field[] fs = clazz.getFields();
        Field[] fs2 = clazz.getDeclaredFields();
        Method[] ms = clazz.getMethods();
        Method[] ms2 = clazz.getDeclaredMethods();

        //通过反射调用运行时的属性
        Field f1 = clazz.getDeclaredField("name");  //可调用任意变量
        f1.setAccessible(true); //允许访问
        f1.set(p, "张三");

        //通过反射调用运行时类的指定方法
        Method m1 = clazz.getMethod("show");
        m1.invoke(p);

        Method m2 = clazz.getDeclaredMethod("disPlay",String.class);
        m2.setAccessible(true);
        m2.invoke(p, "CN");

        System.out.println(p);
    }

    /**
     * 反射源头 : java.lang.Class
     * 创建了一个类，通过编译，生成对应的.class文件，
     * 之后我们使用java加载(JVM的类加载器)，此.class文件
     * 加载到内存以后，就是一个运行时类，存放在缓存区
     * 1、每一个运行时类只加载一次
     * 2、通过Class实例，可以进行如下操作：
     *  - 创建对应的运行时类的对象
     *  - 获取对应的运行时类的完整结构
     *      (属性、方法、构造器、内部类、父类、所在包、异常、注解)
     *  - 调用对应运行时类的指定的结构(属性、方法、构造器)
     *  - 反射的应用，动态代理
     */
    @Test
    public void test02(){
        Person p = new Person();
        //用过运行时类的对象，调用其getClass(),返回其运行时类
        Class clazz = p.getClass();
        System.out.println(clazz);
    }

    /**
     * 如何获取Class的实例
     */
    @Test
    public void test03() throws Exception {
        //1、调用运行时类本身的.class属性
        Class c1 = Person.class;
        System.out.println(c1.getName());
        //2、通过运行时类的对象获取
        Person p = new Person();
        Class c2 = p.getClass();
        System.out.println(c2.getName());
        //3、通过Class的静态方法获取
        Class c3 = Class.forName("reflex.learning02.Person");
        System.out.println(c3.getName());
        //4、通过类的加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        Class c4 = classLoader.loadClass("reflex.learning02.Person");
        System.out.println(c4.getName());

        //一次编译 -> 加入内存 -> 多次使用
        System.out.println(c1 == c2);
        System.out.println(c1 == c3);
        System.out.println(c1 == c4);
    }

    /**
     * 关于类的加载器，ClassLoader
     */
    @Test
    public void test04() throws Exception{
        //系统类加载器
        ClassLoader loader1 = ClassLoader.getSystemClassLoader();
        System.out.println(loader1);
        //扩展类加载器
        ClassLoader loader2 = loader1.getParent();
        System.out.println(loader2);
        //引导类加载器(核心类库，获取不到)
        ClassLoader loader3 = loader2.getParent();
        System.out.println(loader3);

        //自定义类是由系统加载器加载的
        Class c1 = Person.class;
        ClassLoader loader4 = c1.getClassLoader();
        System.out.println(loader4);

        //核心类库，找不到Class信息
        Class c2 = Class.forName("java.lang.Object");
        ClassLoader loader5 = c2.getClassLoader();
        System.out.println(loader5);

        //掌握如下
        ClassLoader loader6 = this.getClass().getClassLoader();
        //从当前项目中
        InputStream in = loader6.getResourceAsStream("jdbc.properties");
        Properties prop = new Properties();
        prop.load(in);
        String name = prop.getProperty("user");
        System.out.println(name);

        //从任意文件中
        FileInputStream fIn = new FileInputStream(new File("/Users/zhanglu/Desktop/lu/workspace/dev_workspace/idea/JavaBase/src/main/resources/jdbc.properties"));
        BufferedReader br = new BufferedReader(new InputStreamReader(fIn, "GBK"));
        prop = new Properties();
        prop.load(br);
        name = prop.getProperty("user");
        System.out.println(name);

    }

}
